package level.model.utility;

import java.awt.Point;
import java.awt.geom.Line2D;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import level.model.Attribute;
import level.model.Collision;
import level.model.Component;
import level.model.GameObject;
import level.model.State;
import level.model.StateCollisionLayer;
import level.model.StateObjectLayer;
import level.model.StateTileLayer;
import level.model.Tile;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class StateLoader
{
	public static State open()
	{
		State state = new State();
		File file = selectFile();
		if (file == null)
			return null;
		state.setFile(file);
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder = null;
		try
		{
			try
			{
				dBuilder = dbFactory.newDocumentBuilder();
			}
			catch (ParserConfigurationException e1)
			{
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			Document doc = null;
			try
			{
				doc = dBuilder.parse(file);
			}
			catch (SAXException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			doc.getDocumentElement().normalize();
			Node mapNode = doc.getFirstChild();
			if (mapNode != null)
			{
				Node node = mapNode.getFirstChild();
				while (node != null)
				{
					if (node.getNodeType() == Node.ELEMENT_NODE
							&& node.getNodeName().equals(
									StateSaver.LAYER_NODE_NAME))
					{
						Node childNode = node.getFirstChild();
						while (childNode != null)
						{
							if (childNode.getNodeType() == Node.ELEMENT_NODE)
							{
								if (childNode.getNodeName().equals(
										StateSaver.COLLISION_LAYER_NODE_NAME))
									loadCollision(state, childNode);
								else if (childNode.getNodeName().equals(
										StateSaver.OBJECT_LAYER_NODE_NAME))
									loadObject(state, childNode);
								else
									loadTile(state, childNode);
							}
							childNode = childNode.getNextSibling();
						}
					}
					node = node.getNextSibling();
				}
			}
		}
		finally
		{
		}
		return state;
	}

	private static void loadCollision(State state, Node parent)
	{
		StateCollisionLayer layer = null;
		Node child = parent.getFirstChild();
		child = child.getNextSibling();
		if (child.getNodeType() == Node.ELEMENT_NODE
				&& child.getNodeName().equals(StateSaver.Z_ORDER_NODE_NAME))
		{
			layer = new StateCollisionLayer(parent.getNodeName(),
					Integer.parseInt(child.getTextContent()));
		}
		state.getLayers().put(layer.getZOrder(), layer);
		child = child.getNextSibling();
		while (child != null)
		{
			if (child.getNodeType() == Node.ELEMENT_NODE
					&& child.getNodeName().equals(
							StateSaver.COLLISION_NODE_NAME))
			{
				Collision coll = new Collision();
				Element e = (Element) child;

				Point p1 = new Point();
				Point p2 = new Point();

				p1.x = Integer.parseInt(getTagValue("x1", e));
				p1.y = Integer.parseInt(getTagValue("y1", e));
				p2.x = Integer.parseInt(getTagValue("x2", e));
				p2.y = Integer.parseInt(getTagValue("y2", e));

				coll.setLine(new Line2D.Float(p1, p2));

				StateHandler.add(layer, coll);
			}
			child = child.getNextSibling();
		}
	}

	private static void loadObject(State state, Node parent)
	{
		StateObjectLayer layer = null;
		Node child = parent.getFirstChild();
		child = child.getNextSibling();
		if (child.getNodeType() == Node.ELEMENT_NODE
				&& child.getNodeName().equals(StateSaver.Z_ORDER_NODE_NAME))
		{
			layer = new StateObjectLayer(parent.getNodeName(),
					Integer.parseInt(child.getTextContent()));
		}
		state.getLayers().put(layer.getZOrder(), layer);
		child = child.getNextSibling();
		while (child != null)
		{
			if (child.getNodeType() == Node.ELEMENT_NODE
					&& child.getNodeName().equals(StateSaver.OBJECT_NODE_NAME))
			{
				GameObject g = new GameObject();
				Element e = (Element) child;

				// Point p1 = new Point();
				// Point p2 = new Point();
				//
				g.setObjectName(getTagValue(StateSaver.NAME_NODE_NAME, e));
				g.setIconPath(getTagValue(StateSaver.IMAGE_NODE_NAME, e));

				g.setX(Integer.parseInt(getTagValue("x", e)));
				g.setY(Integer.parseInt(getTagValue("y", e)));
				g.setW(Integer.parseInt(getTagValue("w", e)));
				g.setH(Integer.parseInt(getTagValue("h", e)));

				NodeList nl = child.getChildNodes();
				for (int i = 0; i < nl.getLength(); i++)
				{
					if (nl.item(i).getNodeType() == Node.ELEMENT_NODE
							&& nl.item(i).getNodeName()
									.equals(StateSaver.COMPONENT_NODE_NAME))
					{
						Component c = new Component();
						e = (Element) nl.item(i);
						c.setComponentName(getTagValue(
								StateSaver.NAME_NODE_NAME, e));

						NodeList al = nl.item(i).getChildNodes();
						for (int j = 0; j < al.getLength(); j++)
						{
							if (al.item(j).getNodeType() == Node.ELEMENT_NODE
									&& al.item(j)
											.getNodeName()
											.equals(StateSaver.ATTRIBUTE_NODE_NAME))
							{
								e = (Element) al.item(j);
								Attribute a = new Attribute(getTagValue(
										StateSaver.NAME_NODE_NAME, e),
										getTagValue(StateSaver.VALUE_NODE_NAME,
												e));
								c.addAttribute(a);
							}
						}

						g.addComponent(c);
					}
				}
				StateHandler.add(layer, g);
			}
			child = child.getNextSibling();
		}
	}

	private static void loadTile(State state, Node parent)
	{
		StateTileLayer layer = null;
		Node child = parent.getFirstChild();
		child = child.getNextSibling();
		if (child.getNodeType() == Node.ELEMENT_NODE
				&& child.getNodeName().equals(StateSaver.Z_ORDER_NODE_NAME))
		{
			layer = new StateTileLayer(parent.getNodeName(),
					Integer.parseInt(child.getTextContent()));
		}
		state.getLayers().put(layer.getZOrder(), layer);
		child = child.getNextSibling();
		while (child != null)
		{
			if (child.getNodeType() == Node.ELEMENT_NODE
					&& child.getNodeName().equals(StateSaver.TILE_NODE_NAME))
			{
				Tile tile = new Tile();
				Element e = (Element) child;

				tile.setIconPath(getTagValue(StateSaver.IMAGE_NODE_NAME, e));
				tile.setX(Integer.parseInt(getTagValue("x", e)));
				tile.setY(Integer.parseInt(getTagValue("y", e)));
				tile.setW(Integer.parseInt(getTagValue("w", e)));
				tile.setH(Integer.parseInt(getTagValue("h", e)));
				tile.setSx(Integer.parseInt(getTagValue("sx", e)));
				tile.setSy(Integer.parseInt(getTagValue("sy", e)));
				tile.setSw(Integer.parseInt(getTagValue("sw", e)));
				tile.setSh(Integer.parseInt(getTagValue("sh", e)));
				StateHandler.add(layer, tile);
			}
			child = child.getNextSibling();
		}
	}

	private static File selectFile()
	{
		FileFilter filter = new FileNameExtensionFilter("XML Filter", "xml");
		JFileChooser fc = new JFileChooser(StateHandler.DEFAULT_DIR);

		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fc.setFileFilter(filter);
		FilenameFilter filter2 = new FilenameFilter()
		{
			public boolean accept(File dir, String name)
			{
				return name.endsWith(".xml");
			}
		};
		int returnVal = fc.showOpenDialog(null);

		if (returnVal != JFileChooser.APPROVE_OPTION)
		{
			return null;
		}
		else
		{
			String[] fileNames = fc.getSelectedFile().getParentFile()
					.list((FilenameFilter) filter2);
			Boolean contains = false;
			if (fileNames.length != 0)
			{
				for (String fileName : fileNames)
				{
					if (fc.getSelectedFile().getName().equals(fileName))
					{
						contains = true;
					}
				}
				if (!contains)
				{
					JOptionPane.showMessageDialog(null, "File does not exist",
							"Error", JOptionPane.ERROR_MESSAGE);
					return null;
				}
			}
		}
		return fc.getSelectedFile();
	}

	private static String getTagValue(String string, Element eElement)
	{
		NodeList nlList = eElement.getElementsByTagName(string).item(0)
				.getChildNodes();
		Node nValue = (Node) nlList.item(0);
		return nValue.getNodeValue();
	}
}
